import json
import random
import re

from django.conf import settings
from django.contrib.auth import login, authenticate, logout
from django.core.mail import send_mail

from carts.utils import merge_cookie_redis_cart
from meiduo_mall.utlis.email import generate_verify_url, decode_token
from meiduo_mall.utlis.login_required import MyLoginRequiredview
from meiduo_mall.utlis.response_code import RET
from users.models import User, Address
from django import http
from django.shortcuts import render, redirect

# Create your views here.
from django.views import View
from django_redis import get_redis_connection

from verifications import constans


class UserRegiserView(View):
    def get(self,request):
        return render(request,'register.html')

    def post(self,request):

        # 1.获取参数
        dict_data=request.POST
        user_name=dict_data.get('user_name')
        pwd=dict_data.get('pwd')
        cpwd=dict_data.get('cpwd')
        phone=dict_data.get('phone')
        msg_code=dict_data.get('msg_code')
        allow=dict_data.get('allow')
        # 2,校验参数
        # 2,1 为空校验
        if not all([user_name,pwd,cpwd,phone,msg_code,allow]):
            return http.HttpResponseBadRequest("参数不全")

        # 2,2 校验用户名格式
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$',user_name):
            return http.HttpResponseBadRequest("用户名格式错误")

        # 2,3 校验密码长度
        if not re.match(r'^[0-9A-Za-z]{8,20}$',pwd):
            return http.HttpResponseBadRequest("密码格式有误")
         # 2,4 判断两次密码是否一致
        if pwd != cpwd:
            return http.HttpResponseBadRequest("两次密码不一致")

        # 2,5 校验手机号格式
        if not re.match(r'^1[345789]\d{9}$',phone):
            return http.HttpResponseBadRequest("手机号格式有误")

        # 2,6 校验短信验证码
        redis_conn=get_redis_connection('code') #获取验证码库
        # verifications/views 有存放短信验证码的方式
        redis_sms_code=redis_conn.get("sms_code_%s"%phone) #当初是以手机号为键,验证码为值存到redis的,现在通过手机号就能获取验证码

        #判断是否过期
        if not redis_sms_code:# 验证码为空即过期
            return http.HttpResponseForbidden('短信验证码过期')
        #正确性匹配
        if msg_code != redis_sms_code.decode():
            return http.HttpResponseForbidden('短信验证码错误')

        # 2,7 校验协议
        if allow != 'on':
            return http.HttpResponseBadRequest("必须同意协议")

        # 3,以上验证都没问题就创建用户,入库
        # 3.1 create_user是User继承的父类的一个方法,可以去他的父类看
        user = User.objects.create_user(username=user_name,password=pwd,mobile=phone)
         #4.返回响应
        login(request, user)
        response = redirect('/')
        response.set_cookie("username", user.username)
        return response
#2.检查用户名是否存在
class CheckUsernameView(View):
    def get(self,request,username):
        #1,根据用户名,查询用户数量
        count = User.objects.filter(username=username).count()

        #2,返回响应
        return http.JsonResponse({"count":count})
#3.检查手机号是否重复
class CheckPhoneView(View):
    def get(self,request,mobile):
        #1,根据手机号,查询用户数量
        count = User.objects.filter(mobile=mobile).count()

        #2,返回响应
        return http.JsonResponse({"count":count})
# 4.登陆视图
class UserLoginView(View):
    def get(self,request):
        return render(request,'login.html')


    def post(self,request):
        #1,获取参数
        username = request.POST.get("username")
        password = request.POST.get("pwd")
        remembered = request.POST.get("remembered")

        #2,校验参数
        #2,1为空校验
        if not all([username,password]):
            return http.HttpResponseForbidden("参数不全")

        #2,2用户名格式校验
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$',username):
            return http.HttpResponseForbidden("用户名格式错误")

        #2,3密码格式校验
        if not re.match(r'^[0-9A-Za-z]{8,20}$',password):
            return http.HttpResponseForbidden("用户密码错误")

        #2,3校验账号,密码正确性(认证),并判断是否认证成功
        user = authenticate(request, username=username, password=password)
        if not user:
            return http.HttpResponseForbidden("用户名或者密码错误")


        # 3,状态保持
        login(request, user)  # 记录用户信息到session

        if remembered == 'on':
            request.session.set_expiry(3600 * 24 * 2)  # 两天有效
        else:
            request.session.set_expiry(0)

        # 4,获取用户名,返回响应
        response = redirect("/") #获取注意内容
        response.set_cookie("username", user.username, max_age=3600 * 24 * 2) #赋值用户名
        response = merge_cookie_redis_cart(request, response, user)
        return response

# 5.退出登陆
class UserLogoutView(View):
    def get(self,request):
        #1,清除session
        logout(request) #官方文档有说明

        #2,清除cookie,username,返回响应
        response = redirect('/')
        response.delete_cookie("username")
        return response
# 6.个人中心,还有三种方式,具体看笔记
class UserCenterInfoView(MyLoginRequiredview):
    def get(self, request):
        # 1,获取用户数据, request.user表示当前登录的用户
        context = {
            "username": request.user.username,
            "mobile": request.user.mobile,
            "email": request.user.email,
            "email_active": False
        }

        # 2,携带数据,渲染页面
        return render(request, 'user_center_info.html', context=context)

#7.邮箱渲染视图
class EmailView(MyLoginRequiredview):
    def put(self,request):
        # 1,获取参数,ajax的请求用body获取,获取到的是字符串
        # json.loads将字符串转字典
        dict_data = json.loads(request.body.decode())
        email = dict_data.get("email")

        #2,校验参数,为空校验
        if not email:
            return http.HttpResponseForbidden("邮箱不能为空")

        #3,数据入库(发送,入库)

            # 3.1获取验证链接,连接包含用户信息
        verify_url = generate_verify_url(request.user)
        # 3.2发送邮件
        # send_mail(subject='美多商城,激活链接',
        #           message='verify_url',
        #           from_email=settings.EMAIL_FROM,
        #           recipient_list=[email]
        #           )

        #用celery发送验证链接
        from celery_tasks.email.tasks import send_verify_url

        send_verify_url.delay(verify_url, email)


        #3.3入库
        request.user.email = email
        request.user.save()

        #4,返回响应
        return http.JsonResponse({"code":RET.OK})

    def get(self, request):
        # 1,获取参数
        token = request.GET.get("token")

        # 2,校验参数
        # 2,1为空校验
        if not token:
            return http.HttpResponseForbidden("非法请求")

        # 2,2正确性,解密校验
        user = decode_token(token)

        if not user:
            return http.HttpResponseForbidden("token失效")

        # 3,数据入库
        user.email_active = True
        user.save()

        # 4,返回响应
        return redirect('/info')
 #渲染用户地址

#8.渲染用户收货地址
class UserAddressView(View):
    def get(self,request):
        # 1,查询用户所有的地址
        # Address.objects.filter(user_id=request.user.id,is_deleted=False).all()
        addresses = request.user.addresses.filter(is_deleted=False)#逻辑删除为0的对象,也就是没被删除的对象

        # 2,拼接数据
        address_list = []
        for address in addresses:
            address_list.append({
                "id":address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province_id": address.province_id,
                "city_id": address.city_id,
                "district_id": address.district_id,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
            })

        # 2,携带数据,渲染页面
        return render(request, 'user_center_site.html', context={"addresses": address_list})


#9.新增地址
class UserAddressCreateView(MyLoginRequiredview):
    def post(self,request):
        #1,获取参数
        dict_data = json.loads(request.body.decode())
        title = dict_data.get("title")
        receiver = dict_data.get("receiver")
        province_id = dict_data.get("province_id")
        city_id = dict_data.get("city_id")
        district_id = dict_data.get("district_id")
        place = dict_data.get("place")
        mobile = dict_data.get("mobile")
        tel = dict_data.get("tel")
        email = dict_data.get("email")

        #2,校验参数,为空校验
        if not all([title,receiver,province_id,city_id,district_id,place,mobile,tel,email]):
            return http.JsonResponse({"code":RET.PARAMERR,"errmsg":"参数不全"})

        #3,数据入库
        dict_data["user"] = request.user
        address = Address.objects.create(**dict_data)

        #4,数据拼接, 返回响应
        context = {
            "code":RET.OK,
            "address":{
                "id": address.id,
                "title":address.title,
                "receiver":address.receiver,
                "province_id":address.province_id,
                "city_id":address.city_id,
                "district_id":address.district_id,
                "place":address.place,
                "mobile":address.mobile,
                "tel":address.tel,
                "email":address.email,
                "province":address.province.name,
                "city":address.city.name,
                "district":address.district.name,
            }
        }
        return http.JsonResponse(context)

#10.修改地址
class UserAddressUpdateView(MyLoginRequiredview):
        def put(self, request, address_id):
            # 1,获取参数
            dict_data = json.loads(request.body.decode())
            title = dict_data.get("title")
            receiver = dict_data.get("receiver")
            province_id = dict_data.get("province_id")
            city_id = dict_data.get("city_id")
            district_id = dict_data.get("district_id")
            place = dict_data.get("place")
            mobile = dict_data.get("mobile")
            tel = dict_data.get("tel")
            email = dict_data.get("email")

            # 2,校验参数
            if not all([title, receiver, province_id, city_id, district_id, place, mobile, tel, email]):
                return http.JsonResponse({"code": RET.PARAMERR, "errmsg": "参数不全"})

            # 3,数据入库
            # Address.objects.filter(id=addredd_id).update(**dict_data)
            address = Address.objects.get(id=address_id)
            address.title = title
            address.receiver = receiver
            address.province_id = province_id
            address.city_id = city_id
            address.district_id = district_id
            address.place = place
            address.mobile = mobile
            address.tel = tel
            address.email = email
            address.save()

            # 4,返回响应
            context = {
                "code": RET.OK,
                "address": {
                    "id": address.id,
                    "title": address.title,
                    "receiver": address.receiver,
                    "province_id": address.province_id,
                    "city_id": address.city_id,
                    "district_id": address.district_id,
                    "place": address.place,
                    "mobile": address.mobile,
                    "tel": address.tel,
                    "email": address.email,
                    "province": address.province.name,
                    "city": address.city.name,
                    "district": address.district.name,
                }
            }
            return http.JsonResponse(context)

        # address_id是前端点击x的时候带过来的
        def delete(self, request, address_id):
            # 1.获取地址对象
            address = Address.objects.get(id=address_id)

            # 2.逻辑删除
            address.is_deleted = True
            address.save()

            #3.返回响应
            return http.JsonResponse({"code":RET.OK})


#11.默认地址
class UserAddressDefaultView(MyLoginRequiredview):
    def put(self,request,address_id):
        #1,设置用户默认地址
        request.user.default_address_id = address_id

        #2,入库
        request.user.save()

        #3,返回响应
        return http.JsonResponse({"code":RET.OK})


#12.修改地址标题
class UserAddressTitleView(MyLoginRequiredview):
    def put(self,request,address_id):
        #1,获取参数,从前端js里边可以看出，前端带来的的是字典格式的内容
        dict_data = json.loads(request.body.decode())
        title = dict_data.get("title")

        #2,校验参数，为空校验
        if not title:
            return http.JsonResponse({"code":RET.PARAMERR})

        #3,数据入库
        ret = Address.objects.filter(id=address_id).update(title=title)
        print(ret)#update方法,返回的是影响的行数,返回0表示没有修改成功
        # address = Address.objects.get(id=address_id)
        # address.title = title
        # address.save()

        #4,返回响应
        return http.JsonResponse({"code":RET.OK})

#13.修改密码
class UserRepasswordView(MyLoginRequiredview):
    def get(self,request):
        return render(request,'user_center_pass.html')

    def post(self,request):
        #1.获取参数
        old_pwd=request.POST.get("old_pwd")
        new_pwd=request.POST.get("new_pwd")
        new_cpwd=request.POST.get("new_cpwd")

        #2.校验参数
        #2.1 为空校验
        if not all([old_pwd,new_pwd,new_cpwd]):
            return http.HttpResponseForbidden("参数不全")
        # 2,2 旧密码正确性
        #.check_password是他父类的父类里边的校验方法
        if not request.user.check_password(old_pwd):
            return http.HttpResponseForbidden("旧密码不对")
        # 2,3 两次新密码是否一致
        if new_pwd != new_cpwd:
            return http.HttpResponseForbidden('两次密码不一致')
        # 2,4 新旧密码是否一致
        if old_pwd == new_pwd:
            return http.HttpResponseForbidden("新旧密码不能一样")

        # 3,重置密码，数据入库，用他爷爷的的方法set——password
        request.user.set_password(new_pwd)
        request.user.save()
        # 4,返回响应
        logout(request)#清除session,状态保持
        response=redirect('/login')#登录界面
        response.delete_cookie('username')#删除对应用户的cookie
        return response



#14.找回密码
class FindPassword(View):
    def get(self,request):
        return render(request,'find_password.html')

#14.1找回密码1
class FindPasswordStp1(View):
    def get(self,request,username):
        #1.获取参数
        image_code=request.GET.get("text")#image_code是js返回的路由里边？的后面=号的前面即(?text=')用户要输入的图片显示的数字
        image_code_id=request.GET.get("image_code_id")#验证码的编号，redis通过他生成随机字符,"image_code_id"是js设定路由里边=号前边的内容
        username=username

        #2.校验参数
        #2.1判空
        if not all([image_code,image_code_id,username]):
            return http.JsonResponse({"code": RET.NODATA, "errmsg": "参数不全"},status = 400)

        #2.1判断是否有该用户
        try:
            if re.match(r'^1[3456789]\d{9}$',username):
                user=User.objects.get(mobile=username)
            else:
                user=User.objects.get(username=username)
        except Exception:
            return http.JsonResponse({"code":RET.DATAERR,"errmsg":"该用户未注册"},status = 404)
        #2.2校验image_code
        redis_conn=get_redis_connection("code")
        pipeline = redis_conn.pipeline()  # 开启管道(事务)

        redis_image_code=redis_conn.get('img_code_%s'%image_code_id)#通过image_code_id获取image_code

        # 判断是否过期,过期redis_image_code会是一个空值
        if not redis_image_code:
            return http.JsonResponse({"code":RET.NODATA,"errmsg":"图片验证码过期"},status = 400)

        #删除redis验证码，避免用户再次使用
        pipeline.delete('img_code_%s'%image_code_id)

        #2.3 图片验证码的真确性
        # 2.4添加lower() 使验证码忽略大小写
        if image_code.lower() !=redis_image_code.decode().lower():
            return http.JsonResponse({"code":RET.DATAERR,"errmsg":"图片验证码错误"},status = 400)

        # users=User.objects.filter(username=username).all()
        mobile=user.mobile

        return http.JsonResponse({"code":RET.OK,"mobile":mobile,"access_token":mobile})

#14.2发送短信验证码
class SmsPush(View):
    def get(self,request):
        mobile=request.GET.get("access_token")


        redis_conn=get_redis_connection("code")
        pipeline=redis_conn.pipeline()#开启事物

        #发送短信验证码，并判断是否发送成功
        #发送前判断是否距离上次发送的时间已经过来60s

        send_fl=redis_conn.get('send_fl_%s'%mobile)
        if send_fl:
            return http.JsonResponse({"code":RET.OK,"errmsg":"短信发送频繁"})
        #随机生成6个验证码
        sms_code="%06d"%random.randint(0,999999)
        print("修改密码的短信验证吗---->%s"%sms_code)

        #导入发送短信函数
        #使用celery发送短信,要启动celery耗时任务才能生效
        #耗时任务就是直接发送短信时，需要等待一段时间，这段时间内不能往下执行命令
        #而使用了耗时任务中的delay方法，则不需要等待就可以机继续往下执行
        #不想用耗时任务可以直接用发送短信的那段代码复制进来用
        from celery_tasks.sms.tasks import send_sms_code
        send_sms_code.delay(mobile,sms_code,constans.REDIS_SMS_COOD_EXPIRES/60)

        # 保存短信验证到redis
        # 保存对象有:手机号,保存5*60s,短信验证码 .以键值对方式存放,可以通过手机号获取验证码

        pipeline.setex("sms_code_%s"%mobile,constans.REDIS_SMS_COOD_EXPIRES,sms_code)#保存并标记，便于再次发送前获取是否未过时

        # 保存并标记短信验证码有效时间为60s,mobile是键,True是代表存在验证码(给验证码做标记)

        pipeline.setex('send_fl_%s'%mobile,60,1)#60s后这个键值对就不存在,1代表True,但是这里不能写True,redis 3.2.1 版本不支持bool类型,所以用1

        # 提交管道(事务)
        pipeline.execute()
        # 4,返回响应
        return http.JsonResponse({"code": RET.OK, "errmsg": "ok"})

#14.3校验验证码并进入第三步
class SmsCheck(View):
    def get(self,request,username):
        #1.获取参数
        sms_code=request.GET.get("sms_code")
        username=username
        user = User.objects.get(username=username)
        mobile=user.mobile

        #2.校验验证码
        redis_conn=get_redis_connection("code")
        redis_sms_code=redis_conn.get("sms_code_%s"%mobile)


        if sms_code != redis_sms_code.decode():
            return http.JsonResponse({"code":RET.DATAERR,"errmsg":"验证码错误"},status = 400)

        #3.数据返回
        user_id = user.id
        return http.JsonResponse({"code":RET.OK,"user_id":user_id,"access_token":" "})



class PasswordReset(View):
    def post(self,request,user_id):
        #1.获取数据
        user_id=user_id
        dict_data=json.loads(request.body.decode())
        new_password=dict_data.get("password")

        # access_token=request.GET.get("access_token")

        #2.校验参数
        if not all([user_id,new_password]):
            return http.JsonResponse({"code":RET.DATAERR,"message":"参数不全"})


        user = User.objects.get(id=user_id)
        user.set_password(new_password)
        user.save()

        return http.JsonResponse({"code":RET.OK})



